#include <windows.h>
#include <vector>
#include <iostream>
#include <string>
#include <shlwapi.h> // 添加路径检查所需头文件

#pragma comment(lib, "shlwapi.lib") // 链接shlwapi库

// 如果RT_MANIFESTW未定义，则手动定义它
#ifndef RT_MANIFESTW
#define RT_MANIFESTW MAKEINTRESOURCEW(24)
#endif

// 使用Windows API读取文件（支持宽路径）
std::vector<BYTE> ReadFileToBytes(const std::wstring& filePath) {
    std::cout << "尝试打开文件: ";
    for (wchar_t c : filePath) std::cout << static_cast<char>(c);
    std::cout << std::endl;

    HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        DWORD error = GetLastError();
        std::string msg = "无法打开文件，错误码：" + std::to_string(error);
        std::cout << msg << std::endl;
        throw std::runtime_error(msg);
    }

    // 获取文件大小
    DWORD fileSize = GetFileSize(hFile, NULL);
    if (fileSize == INVALID_FILE_SIZE) {
        DWORD error = GetLastError();
        CloseHandle(hFile);
        std::string msg = "获取文件大小失败，错误码：" + std::to_string(error);
        std::cout << msg << std::endl;
        throw std::runtime_error(msg);
    }

    // 读取文件内容
    std::vector<BYTE> buffer(fileSize);
    DWORD bytesRead;
    if (!ReadFile(hFile, buffer.data(), fileSize, &bytesRead, NULL) || bytesRead != fileSize) {
        DWORD error = GetLastError();
        CloseHandle(hFile);
        std::string msg = "读取文件失败，错误码：" + std::to_string(error);
        std::cout << msg << std::endl;
        throw std::runtime_error(msg);
    }

    CloseHandle(hFile);
    std::cout << "文件读取成功，大小: " << fileSize << " 字节" << std::endl;
    return buffer;
}

// 将UTF-8字符串转换为UTF-16
std::vector<WCHAR> Utf8ToUtf16(const std::string& utf8) {
    int len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr, 0);
    if (len == 0) {
        DWORD error = GetLastError();
        std::string msg = "UTF-8转UTF-16失败，错误码：" + std::to_string(error);
        std::cout << msg << std::endl;
        throw std::runtime_error(msg);
    }
    std::vector<WCHAR> utf16(len);
    MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, utf16.data(), len);
    std::cout << "UTF-8转UTF-16成功，长度: " << len << std::endl;
    return utf16;
}

// 向已编译的EXE中嵌入清单文件
bool EmbedManifest(const std::wstring& exePath, const std::wstring& manifestPath) {
    try {
        std::cout << "\n开始嵌入清单..." << std::endl;
        std::cout << "目标EXE: ";
        for (wchar_t c : exePath) std::cout << static_cast<char>(c);
        std::cout << std::endl;

        std::cout << "清单文件: ";
        for (wchar_t c : manifestPath) std::cout << static_cast<char>(c);
        std::cout << std::endl;

        // 1. 读取清单文件内容（UTF-8）
        auto manifestBytes = ReadFileToBytes(manifestPath);
        std::string manifestUtf8(reinterpret_cast<char*>(manifestBytes.data()), manifestBytes.size());

        // 2. 转换为UTF-16
        auto manifestUtf16 = Utf8ToUtf16(manifestUtf8);
        DWORD manifestSize = manifestUtf16.size() * sizeof(WCHAR);

        // 3. 打开EXE，准备更新资源
        std::cout << "尝试打开EXE进行资源更新..." << std::endl;
        HANDLE hUpdate = BeginUpdateResourceW(exePath.c_str(), FALSE);
        if (hUpdate == INVALID_HANDLE_VALUE) {
            DWORD error = GetLastError();
            std::string msg = "打开EXE失败，错误码：" + std::to_string(error);
            std::cout << msg << std::endl;
            throw std::runtime_error(msg);
        }

        // 4. 添加RT_MANIFEST类型资源
        std::cout << "尝试更新资源..." << std::endl;
        BOOL success = UpdateResourceW(
            hUpdate,
            RT_MANIFESTW,              // 使用宽字符版本
            MAKEINTRESOURCEW(1),       // 资源ID：1
            MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
            reinterpret_cast<LPVOID>(manifestUtf16.data()),
            manifestSize
        );

        if (!success) {
            DWORD err = GetLastError();
            EndUpdateResourceW(hUpdate, TRUE);
            std::string msg = "更新资源失败，错误码：" + std::to_string(err);
            std::cout << msg << std::endl;
            throw std::runtime_error(msg);
        }

        // 5. 提交修改并关闭
        std::cout << "尝试提交资源修改..." << std::endl;
        if (!EndUpdateResourceW(hUpdate, FALSE)) {
            DWORD error = GetLastError();
            std::string msg = "提交修改失败，错误码：" + std::to_string(error);
            std::cout << msg << std::endl;
            throw std::runtime_error(msg);
        }

        std::cout << "清单嵌入成功！" << std::endl;
        return true;
    } catch (const std::exception& e) {
        std::cerr << "操作失败：" << e.what() << std::endl;
        std::cout << "操作失败：" << e.what() << std::endl; // 同时输出到标准输出
        return false;
    }
}

// main函数（处理命令行参数）
int main(int argc, char* argv[]) {
    std::cout << "清单嵌入工具启动..." << std::endl;

    if (argc != 3) {
        std::cout << "用法：EmbedManifest.exe <目标EXE路径> <清单文件路径>" << std::endl;
        return 1;
    }

    std::cout << "参数解析:" << std::endl;
    std::cout << "  参数1(EXE路径): " << argv[1] << std::endl;
    std::cout << "  参数2(清单路径): " << argv[2] << std::endl;

    // 将char*路径转换为wstring（多字节转宽字符）
    std::wstring exePath;
    std::wstring manifestPath;
    
    std::cout << "\n转换EXE路径为宽字符..." << std::endl;
    int len = MultiByteToWideChar(CP_ACP, 0, argv[1], -1, nullptr, 0);
    if (len > 0) {
        exePath.resize(len - 1); // 减去结尾的空字符
        MultiByteToWideChar(CP_ACP, 0, argv[1], -1, &exePath[0], len);
        std::cout << "EXE路径转换成功" << std::endl;
    } else {
        DWORD error = GetLastError();
        std::cout << "EXE路径转换失败，错误码：" << error << std::endl;
        return 1;
    }

    std::cout << "转换清单路径为宽字符..." << std::endl;
    len = MultiByteToWideChar(CP_ACP, 0, argv[2], -1, nullptr, 0);
    if (len > 0) {
        manifestPath.resize(len - 1);
        MultiByteToWideChar(CP_ACP, 0, argv[2], -1, &manifestPath[0], len);
        std::cout << "清单路径转换成功" << std::endl;
    } else {
        DWORD error = GetLastError();
        std::cout << "清单路径转换失败，错误码：" << error << std::endl;
        return 1;
    }

    // 检查文件是否存在
    std::cout << "\n检查文件是否存在..." << std::endl;
    if (!PathFileExistsW(exePath.c_str())) {
        std::cout << "错误：EXE文件不存在" << std::endl;
        return 1;
    }
    if (!PathFileExistsW(manifestPath.c_str())) {
        std::cout << "错误：清单文件不存在" << std::endl;
        return 1;
    }
    std::cout << "文件检查通过" << std::endl;

    // 执行清单嵌入
    if (EmbedManifest(exePath, manifestPath)) {
        std::cout << "\n操作成功：清单已成功嵌入到 " << argv[1] << std::endl;
        return 0;
    } else {
        std::cout << "\n操作失败：清单嵌入失败" << std::endl;
        return 1;
    }
}


// g++ manifest_add.cpp -o manifest_add.exe -lole32 -luuid -lshlwapi -mconsole